[I've bcc'ed a bunch of people on this -- don't be suprised if you got it unsolicited.] Summary: This message contains a patch that is strongly believed by me to fix the very nasty security vulnerability in most systems described a few hours ago by the 8lgm people in the syslog client code -- see [8lgm]-Advisory-22.UNIX.syslog.2-Aug-1995. The patch works on 4.4lite BSD derived systems but should be easily modified to work on other systems. [You can go to http://www.8lgm.org/ to get a copy of the 8lgm report.] This vulnerability is one that allows people to break in to a machine via sendmail and apparently, from a cursory glance, several other daemons -- it also appears to allow people to break in to machines running sendmail through most kinds of firewalls. Warning: I have not been able to test the patch against every possible variation on this particular bug. Caveat emptor! I'm releasing it now because it seemed too important to delay. The patch isn't as elegant as it could be but I wrote it in the middle of the night -- please forgive the excessive tests and the like as I was trying to be conservative and I was tired. This is a context diff to syslog.c in NetBSD-current's libc, which is essentially the same as the syslog.c in any other 4.4lite based system, such as BSDI, FreeBSD, etc. The patch prevents any program calling syslog(3) from overflowing its buffer, and thus prevents all sorts of nasty fandango-on-stack based evils. The 8lgm advisory suggested that programs could check the length of their parameters to syslog themselves but I could find no reasonable way to do that given that parameters like "%m" result in unpredictable length expansions. The patch should also work on other operating systems provided you 1) are running a 4.4 compatible syslog, 2) get a whole copy of libc/gen/syslog.c from your friendly neighborhood copy of 4.4lite or a descendant, and 3) have snprintf on your system or can find a version of snprintf that will run on your system. I have not tested it on machines other than 4.4lite based boxes, however. If you have something else, you are on your own. Unfortunately, there is no obvious way to fix this problem without snprintf. If you are on a Sun or similar platform, you'd better find a copy of an snprintf from somewhere. The following short stretch of code, produced by John Hawkinson, (and distributed without his formal permission; I doubt he'll mind, though) will test whether your machine has what I believe to be the problem documented by 8lgm. It should coredump or something similar on machines with the problem -- it should work just fine (albeit with a truncated log message) on machines that have been patched. Following the test program is the patch. Note that just because this program works doesn't mean you aren't necessarily vulnerable! --cut here-------------------------------------------------------------------- /* Test program by jhawk@mit.edu -- comment by perry@piermont.com */ #include <syslog.h> void Amain () { char buffer[4096]; int i; for (i=0; i<4000; i++) buffer[i]=65; syslog(LOG_ERR, buffer); } void main() { Amain(); } --cut here-------------------------------------------------------------------- And here is the patch. --cut here-------------------------------------------------------------------- *** syslog.c.orig Tue Apr 11 05:55:22 1995 --- syslog.c Tue Aug 29 04:11:25 1995 *************** *** 105,109 **** time_t now; int fd, saved_errno; ! char *stdp, tbuf[2048], fmt_cpy[1024]; #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID --- 105,119 ---- time_t now; int fd, saved_errno; ! ! #define TBUFLEN 2048 ! #define FMT_CPYLEN 1024 ! ! int tbuf_len, fmt_cpy_len; ! int prlen; ! char *stdp, tbuf[TBUFLEN], fmt_cpy[FMT_CPYLEN]; ! ! tbuf_len = TBUFLEN; ! fmt_cpy_len = FMT_CPYLEN; ! #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID *************** *** 127,144 **** /* Build the message. */ (void)time(&now); ! p = tbuf + sprintf(tbuf, "<%d>", pri); ! p += strftime(p, sizeof (tbuf) - (p - tbuf), "%h %e %T ", localtime(&now)); if (LogStat & LOG_PERROR) stdp = p; if (LogTag == NULL) LogTag = __progname; - if (LogTag != NULL) - p += sprintf(p, "%s", LogTag); - if (LogStat & LOG_PID) - p += sprintf(p, "[%d]", getpid()); if (LogTag != NULL) { *p++ = ':'; *p++ = ' '; } --- 137,176 ---- /* Build the message. */ (void)time(&now); ! prlen = snprintf(tbuf, tbuf_len, "<%d>", pri); ! tbuf_len -= prlen; ! if (tbuf_len < 0) ! tbuf_len = 0; ! p = tbuf + prlen; ! prlen = strftime(p, tbuf_len, "%h %e %T ", localtime(&now)); + tbuf_len -= prlen; + if (tbuf_len < 0) + tbuf_len = 0; + p += prlen; if (LogStat & LOG_PERROR) stdp = p; if (LogTag == NULL) LogTag = __progname; if (LogTag != NULL) { + prlen = snprintf(p, tbuf_len, "%s", LogTag); + tbuf_len -= prlen; + if (tbuf_len < 0) + tbuf_len = 0; + p += prlen; + } + + if (LogStat & LOG_PID) { + prlen = snprintf(p, tbuf_len, "[%d]", getpid()); + tbuf_len -= prlen; + if (tbuf_len < 0) + tbuf_len = 0; + p += prlen; + } + if ((LogTag != NULL) && (tbuf_len > 2)) { *p++ = ':'; *p++ = ' '; + tbuf_len -= 2; + if (tbuf_len < 0) + tbuf_len = 0; } *************** *** 147,156 **** if (ch == '%' && fmt[1] == 'm') { ++fmt; ! t += sprintf(t, "%s", strerror(saved_errno)); ! } else ! *t++ = ch; ! *t = '\0'; - p += vsprintf(p, fmt_cpy, ap); cnt = p - tbuf; --- 179,202 ---- if (ch == '%' && fmt[1] == 'm') { ++fmt; ! prlen = snprintf(t, fmt_cpy_len, ! "%s", strerror(saved_errno)); ! t += prlen; ! fmt_cpy_len -= prlen; ! } else { ! if (fmt_cpy_len-- > 0) ! *t++ = ch; ! } ! ! if (fmt_cpy_len-- > 0) ! *t = '\0'; ! else ! fmt_cpy[FMT_CPYLEN-1] = '\0'; /* crock, but what else to do? */ ! ! prlen = vsnprintf(p, tbuf_len, fmt_cpy, ap); ! tbuf_len -= prlen; ! if (tbuf_len < 0) ! tbuf_len = 0; ! p += prlen; cnt = p - tbuf; --cut here--------------------------------------------------------------------